/*
					ORDENACAO DE VETOR UTILIZANDO SOCKETS EM C
 =======================================================================================
 Nome        : NivelInt1.c
 Autores     : Eduardo Aramizu, Matheus Zoccoler
 Disciplina  : SSC0640 – Sistemas Computacionais Distribuídos (Engenharia de Computacao)
 Descricao   : Nesse programa definimos os nos do nivel 2 do cluster. Cada no aguarda
				o recebimento dos dois vetores de tamanho 125 gerados nos nos do nivel 1
				(NivelInf.c). O programa entao junta as duas partes e ordena pelo metodo
				QuickSort. O vetor ordenado e' enviado ao no superior (NivelInt2.c). Por
				fim, o programa fica aguardando o recebimento do vetor de 1000 posicoes 
				ja ordenado e o reenvia aos nos inferiores.			
 =======================================================================================
 */
#ifndef ALL_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>   // Definicao de tipos
#include <sys/socket.h>  // Biblioteca de estrutara de sockets
#include <netinet/in.h>  // Define os padroes de protocolo IP
#include <arpa/inet.h>   // Converte enderecos hosts
#include <string.h>
#include <unistd.h> // Define constantes POSIX
#include <errno.h>  // Utilizado para capturar mensagens de erro
#include <netdb.h>
#define ALL_H_INCLUDED
#endif

#ifndef OPERATIONS_H_INCLUDED
#include "Operacoes.h"
#endif

#define RCVSIZE 125
#define TOTSIZE 1000

int main(int argc, char *argv[])
{
    int sockInf, sockSup, connected_r, connected_l, bytes_recv, i, true = 1;
    int rcvArray[2*RCVSIZE],totArray[TOTSIZE];
    struct sockaddr_in server_addr, client_addr;
    int sin_size;
    struct hostent *host;

    struct timeval t1,t2;
    int time;
    FILE *f;
    f = fopen(argv[4], "w");

    /********** INICIA SERVIDOR NA PORTA ESPECIFICADA ************/
    if ((sockInf = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("Erro de Socket");
        exit(1);
    }

    if (setsockopt(sockInf, SOL_SOCKET, SO_REUSEADDR, &true,sizeof(int)) == -1)
    {
        perror("Erro Setsockopt");
        exit(1);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[3]));    //Servidor escuta na porta de argv[3]
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero),8);

    if (bind(sockInf, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
    {
        perror("Nao foi possivel realizar o bind");
        exit(1);
    }


    if (listen(sockInf, 10) == -1)
    {
        perror("Erro de Listen");
        exit(1);
    }

    /********** RECEBE DOS NIVEIS INF ************/
    printf("\nServidor TCP esperando por conexoes na porta %s\n", argv[3]);

    int cont = 0;
    while(cont < 2)
    {
        sin_size = sizeof(struct sockaddr_in);

        if(cont == 0)
        {
            connected_l = accept(sockInf, (struct sockaddr *)&client_addr, &sin_size);
            printf("\nConexão aceita de (%s , %d)\n", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));

            gettimeofday(&t1, 0);   //Tempo antes do receive
            bytes_recv=recv(connected_l,rcvArray,RCVSIZE*4, MSG_WAITALL);
            gettimeofday(&t2, 0);   //Tempo depois do receive
            time = t2.tv_usec-t1.tv_usec;
            fprintf(f,"\nTempo recebimento do inferior 1:  %.2dus.\n", time);
        }
        else
        {
            connected_r = accept(sockInf, (struct sockaddr *)&client_addr, &sin_size);
            printf("\nConexão aceita de (%s , %d)\n", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));

            gettimeofday(&t1, 0);   //Tempo antes do receive
            bytes_recv=recv(connected_r,rcvArray+RCVSIZE,RCVSIZE*4, MSG_WAITALL);
            gettimeofday(&t2, 0);   //Tempo depois do receive
            time = t2.tv_usec-t1.tv_usec;
            fprintf(f,"\nTempo recebimento do inferior 2:  %.2dus.\n", time);
        }

        printf("\nMensagem recebida: \n");

        cont++;
    }

    /********** ORDENA VETORES RECEBIDOS ************/
    gettimeofday(&t1, 0);   //Tempo antes da ordenacao
    quickSort(rcvArray, 2*RCVSIZE);
    gettimeofday(&t2, 0);   //Tempo depois da ordenacao
    time = t2.tv_usec-t1.tv_usec;
    fprintf(f,"\nTempo ordenacao:  %.2dus.\n", time);

    /*for(i=0; i<2*RCVSIZE; i++)
        printf("%d\n", rcvArray[i]);*/

    /********** CONECTA COM NIVEL SUP ************/
    host = gethostbyname(argv[1]);

    if ((sockSup = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("Erro no Socket");
        exit(1);
    }

    server_addr.sin_port = ntohs(atoi(argv[2]));    //Conecta na porta de argv[2]
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);

    if (connect(sockSup, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
    {
        perror("Erro de conexao");
        exit(1);
    }

    /********** ENVIA AO NIVEL SUP ************/
    gettimeofday(&t1, 0);   //Tempo antes do send
    // Função send(int socket, void*buffer, size_t size, int flags)
    send(sockSup, rcvArray, (2*RCVSIZE)*4, 0);
    gettimeofday(&t2, 0);   //Tempo depois do send
    time = t2.tv_usec-t1.tv_usec;
    fprintf(f,"\nTempo envio para %s:  %.2dus.\n", argv[1], time);

    printf("Mensagem enviada para %s na porta %s.\n", argv[1], argv[2]);

    /********** RECEBE DO NIVEL SUP ************/
    gettimeofday(&t1, 0);   //Tempo antes do receive
    // Funcao recv (int socket, void *buffer, size_t size, int flags)
    if( bytes_recv = recv(sockSup, totArray, TOTSIZE*4, MSG_WAITALL) != 0)
        printf("\n Mensagem recebida: \n");
    else
        printf("\n Erro ao receber mensagem. \n");
    gettimeofday(&t2, 0);   //Tempo depois do receive
    time = t2.tv_usec-t1.tv_usec;
    fprintf(f,"\nTempo recebimento de %s:  %.2dus.\n", argv[1], time);

    /*for(i=0; i<TOTSIZE; i++)
        printf("%d\n", totArray[i]);*/

    //Fecha conexao com superior
    close(sockSup);

    /********** ENVIA AOS NIVEIS INF ************/
    gettimeofday(&t1, 0);   //Tempo antes do send
    send(connected_l,totArray,TOTSIZE*4, 0);
    gettimeofday(&t2, 0);   //Tempo depois do send
    time = t2.tv_usec-t1.tv_usec;
    fprintf(f,"\nTempo envio ao inferior 1:  %.2dus.\n", time);

    gettimeofday(&t1, 0);   //Tempo antes do send
    send(connected_r,totArray,TOTSIZE*4, 0);
    gettimeofday(&t2, 0);   //Tempo depois do send
    time = t2.tv_usec-t1.tv_usec;
    fprintf(f,"\nTempo envio ao inferior 2:  %.2dus.\n", time);

    //Fecha conexoes com inferiores
    close(connected_l);
    close(connected_r);

    fclose(f);

    return 0;
}
